/// <reference path="../../feng3d/out/feng3d.d.ts" />
/// <reference path="../../physics/out/physics.d.ts" />

var dt = 1 / 60, R = 0.2;

var clothMass = 1;  // 1 kg in total
var clothSize = 1; // 1 meter
var Nx = 12;
var Ny = 12;
var mass = clothMass / Nx * Ny;

var restDistance = clothSize / Nx;

var ballSize = 0.1;

var clothFunction = plane(restDistance * Nx, restDistance * Ny);

function plane(width, height)
{
    return function (u, v)
    {
        var x = (u - 0.5) * width;
        var y = (v + 0.5) * height;
        var z = 0;
        return new feng3d.Vector3(x, y, z);
    };
}

var particles = [];

var engine = new feng3d.Engine();
engine.scene = feng3d.Engine.createNewScene();
var camera = engine.scene.getComponentsInChildren(feng3d.Camera)[0];
camera.transform.z = -Math.sin(Math.PI / 4) * 3;
camera.transform.y = 0;

camera.gameObject.addComponent(feng3d.FPSController);

var world = engine.scene.getComponent(physics.PhysicsWorld).world;

var object = new feng3d.GameObject();;
var m = object.addComponent(feng3d.MeshModel);
m.material = feng3d.serialization.setValue(new feng3d.Material(), {
    uniforms: { s_diffuse: { source: { url: 'sunflower.jpg' }, flipY: false, anisotropy: 16 } }
});
var clothGeometry = m.geometry = new feng3d.ParametricGeometry(clothFunction, Nx, Ny, true);
engine.scene.gameObject.addChild(object);

var sphereMesh = feng3d.gameObjectFactory.createSphere();
engine.scene.gameObject.addChild(sphereMesh);
var sphereGeometry = sphereMesh.getComponent(feng3d.MeshModel).geometry;
sphereGeometry.radius = ballSize;
sphereGeometry.segmentsH = 20;
sphereGeometry.segmentsW = 20;
var sphereBody = sphereMesh.getComponent(physics.Rigidbody).body;
sphereMesh.getComponent(physics.SphereCollider).radius = ballSize;

var planeMesh = feng3d.gameObjectFactory.createPlane();
engine.scene.gameObject.addChild(planeMesh);
planeMesh.getComponent(physics.Rigidbody).body.position.y = -1;

initCannon();
animate();

function initCannon()
{
    world.broadphase = new physics.NaiveBroadphase();
    world.gravity.set(0, -9.82, 0);
    world.solver.iterations = 20;

    // Materials
    var clothMaterial = new physics.Material();
    var sphereMaterial = new physics.Material();
    var clothSphereContactMaterial = new physics.ContactMaterial(clothMaterial,
        sphereMaterial,
        { friction: 0.0, restitution: 0.0 }
    );
    // Adjust constraint equation parameters for ground/ground contact
    clothSphereContactMaterial.contactEquationStiffness = 1e9;
    clothSphereContactMaterial.contactEquationRelaxation = 3;

    // Add contact material to the world
    world.addContactMaterial(clothSphereContactMaterial);

    // Create cannon particles
    for (var i = 0, il = Nx + 1; i !== il; i++)
    {
        particles.push([]);
        for (var j = 0, jl = Ny + 1; j !== jl; j++)
        {
            var idx = j * (Nx + 1) + i;
            var p = clothFunction(i / (Nx + 1), j / (Ny + 1));
            var particle = new physics.Body({
                mass: j == Ny ? 0 : mass
            });
            particle.addShape(new physics.Particle());
            particle.linearDamping = 0.5;
            particle.position.set(
                p.x,
                p.y - Ny * 0.9 * restDistance,
                p.z
            );
            particles[i].push(particle);
            world.addBody(particle);
            particle.velocity.set(0, 0, -0.1 * (Ny - j));
        }
    }
    function connect(i1, j1, i2, j2)
    {
        world.addConstraint(new physics.DistanceConstraint(particles[i1][j1], particles[i2][j2], restDistance));
    }
    for (var i = 0; i < Nx + 1; i++)
    {
        for (var j = 0; j < Ny + 1; j++)
        {
            if (i < Nx) connect(i, j, i + 1, j);
            if (j < Ny) connect(i, j, i, j + 1);
        }
    }
}

function animate()
{
    requestAnimationFrame(animate);
    var t = Date.now() / 10 * Math.DEG2RAD;
    sphereBody.position.set(R * Math.sin(t), 0, R * Math.cos(t));
    render();
}

function render()
{
    var positions = clothGeometry.positions;
    for (var i = 0, il = Nx + 1; i !== il; i++)
    {
        for (var j = 0, jl = Ny + 1; j !== jl; j++)
        {
            var idx = j * (Nx + 1) + i;
            var position = particles[i][j].position;
            positions[idx * 3 + 0] = position.x;
            positions[idx * 3 + 1] = position.y;
            positions[idx * 3 + 2] = position.z;
        }
    }
    clothGeometry.positions = positions;
    clothGeometry.invalidateGeometry();
}